using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Reflection;
using GwtRpcNet.Service;
using System.Configuration;

namespace GwtRpcNet.Rpc
{
    public class RpcNet
    {
        #region Static Members

        public const string SERIALIZED_REFERENCE_SEPARATOR = "/";
        public const string CUSTOM_SERIALIZER = "_CustomFieldSerializer";
        public const string GWTNET_CUSTOM_SERIALIZER_SPACE = "GwtRpcNet.CustomFieldSerializers";

        public static Dictionary<string, Type> TYPE_NAMES = null;
        public static Dictionary<Type, string> TYPE_NAMES_SERIALIZED = null;
        public static Dictionary<string, Type> DOTNET_COLLECTION_NAMES = null;
        public static Dictionary<Type, int> TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES = null;
        static RpcNet()
        {
            TYPE_NAMES = new Dictionary<string,Type>();
            TYPE_NAMES.Add("Z", typeof(bool));
            TYPE_NAMES.Add("B", typeof(byte));
            TYPE_NAMES.Add("C", typeof(char));
            TYPE_NAMES.Add("D", typeof(double));
            TYPE_NAMES.Add("F", typeof(float));
            TYPE_NAMES.Add("I", typeof(int));
            TYPE_NAMES.Add("J", typeof(long));
            TYPE_NAMES.Add("S", typeof(short));
            TYPE_NAMES.Add("java.lang.Boolean", typeof(bool));
            TYPE_NAMES.Add("java.lang.Byte", typeof(byte));
            TYPE_NAMES.Add("java.lang.Char", typeof(char));
            TYPE_NAMES.Add("java.lang.Double", typeof(double));
            TYPE_NAMES.Add("java.lang.Float", typeof(float));
            TYPE_NAMES.Add("java.lang.Integer", typeof(int));
            TYPE_NAMES.Add("java.lang.Long", typeof(long));
            TYPE_NAMES.Add("java.lang.Short", typeof(short));
            TYPE_NAMES.Add("java.lang.String", typeof(string));

            TYPE_NAMES_SERIALIZED = new Dictionary<Type, string>();
            TYPE_NAMES_SERIALIZED.Add(typeof(bool), "Z");
            TYPE_NAMES_SERIALIZED.Add(typeof(byte), "B");
            TYPE_NAMES_SERIALIZED.Add(typeof(char), "C");
            TYPE_NAMES_SERIALIZED.Add(typeof(double), "D");
            TYPE_NAMES_SERIALIZED.Add(typeof(float), "F");
            TYPE_NAMES_SERIALIZED.Add(typeof(int), "I");
            TYPE_NAMES_SERIALIZED.Add(typeof(long), "J");
            TYPE_NAMES_SERIALIZED.Add(typeof(short), "S");
            TYPE_NAMES_SERIALIZED.Add(typeof(IList), "java.util.List");
            TYPE_NAMES_SERIALIZED.Add(typeof(ArrayList), "java.util.ArrayList");
            TYPE_NAMES_SERIALIZED.Add(typeof(DateTime), "java.util.Date");

            DOTNET_COLLECTION_NAMES = new Dictionary<string, Type>();
            DOTNET_COLLECTION_NAMES.Add("java.util.List", typeof(IList));
            DOTNET_COLLECTION_NAMES.Add("java.util.ArrayList", typeof(ArrayList));
            DOTNET_COLLECTION_NAMES.Add("java.util.Date", typeof(DateTime));

            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES = new Dictionary<Type, int>();
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(bool), 0);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(byte), 1);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(char), 2);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(double), 3);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(Exception), 4);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(float), 5);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(int), 6);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(long), 7);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(object), 8);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(short), 9);
            TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.Add(typeof(string), 10);
        }

        public static RpcNetReader Reader
        {
            get { return new RpcNetReader(); }
        }

        public static RpcNetWriter Writer
        {
            get { return new RpcNetWriter(); }
        }

        public static GwtRpcNetServiceInfo ServiceInfo
        {
            get { return (GwtRpcNetServiceInfo)ConfigurationManager.GetSection("gwtRpcNetService"); }
        }

        #endregion

        private static Hashtable CUSTOM_SERIALIZERS = Hashtable.Synchronized(new Hashtable());

        public static RpcNetRequest DecodeRequest(string request)
        {
            return Reader.DecodeRequest(request);
        }

        public static string HandleRequest(RpcNetRequest decoded)
        {
            return HandleRequest(decoded.GetService(), decoded);
        }

        public static string HandleRequest(object instance, RpcNetRequest req)
        {
            MethodInfo info = req.GetServiceMethod();
            object result = info.Invoke(instance, req.ServiceParamValues);
            return Writer.EncodeResponse(info, req, result, false);
        }

        public static Type HasCustomFieldSerializer(Type instanceType)
        {
            if (CUSTOM_SERIALIZERS.ContainsKey(instanceType))
                return (Type)CUSTOM_SERIALIZERS[instanceType];
            Type result = ComputeHasCustomFieldSerializer(instanceType);
            if (result != null)
                CUSTOM_SERIALIZERS.Add(instanceType, result);
            return result;
        }

        private static Type ComputeHasCustomFieldSerializer(Type instanceType) 
        {
            string qualifiedTypeName;

            if (instanceType.IsArray) 
                throw new NotImplementedException();
            else 
                qualifiedTypeName = instanceType.FullName;

            string simpleSerializerName = qualifiedTypeName + CUSTOM_SERIALIZER;
            Type customSerializer = Type.GetType(simpleSerializerName);
            if (customSerializer != null) 
                return customSerializer;

            string customFieldSerializer = GWTNET_CUSTOM_SERIALIZER_SPACE + "." +
                instanceType.Name + CUSTOM_SERIALIZER;
            Type t = Type.GetType(customFieldSerializer);
            if (t != null)
                return t;
            return null;
        }

        public static bool IsInstantiable(Type valueType)
        {
            if (valueType.IsPrimitive)
                return true;
            if (valueType.IsArray)
                throw new NotImplementedException();
            if (valueType.IsInterface)
                return false;
            if (valueType.IsSerializable)
                return true;
            return HasCustomFieldSerializer(valueType) != null;
        }

        public static Type GetTypeForName(string aTypeName)
        {
            string typeName = aTypeName;
            int ind = typeName.IndexOf('/');
            if (ind > -1)
                typeName = typeName.Substring(0, ind);

            if (TYPE_NAMES.ContainsKey(typeName))
                return TYPE_NAMES[typeName];
            if (DOTNET_COLLECTION_NAMES.ContainsKey(typeName))
                return DOTNET_COLLECTION_NAMES[typeName];

            Type t = Type.GetType(typeName);
            if (t != null)
                return t;

            string libName = ServiceInfo.LibraryName;
            if (!string.IsNullOrEmpty(libName))
                t = Type.GetType(typeName + ", " + libName);
            return t;
        }

        public static bool IsPrimitiveType(Type valueType)
        {
            return (valueType == typeof(bool) || valueType == typeof(byte) || valueType == typeof(char)
                || valueType == typeof(double) || valueType == typeof(float) || valueType == typeof(int)
                || valueType == typeof(long) || valueType == typeof(short) || valueType == typeof(string));
        }
    }

    public class PropertyInfoComparer : IComparer<PropertyInfo>
    {
        public int Compare(PropertyInfo x, PropertyInfo y)
        {
            return x.Name.CompareTo(y.Name);
        }
    }
}
